﻿using engine;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace engine
{
    public class TemplateConfig
    {
        private FuncNode _rootFuncNode;

        public Dictionary<string, TypeItem> Types { get; set; }

        public Dictionary<string, VarItem> Vars { get; set; }

        public Dictionary<string, FuncNode> Macros { get; set; }

        public TemplateConfigOptions Options { get; set; }

        public TemplateConfig(string configName, FuncNode rootFuncNode)
        {
            Types = new Dictionary<string, TypeItem>();
            Vars = new Dictionary<string, VarItem>();
            Macros = new Dictionary<string, FuncNode>();
            Options = new TemplateConfigOptions();
            _rootFuncNode = rootFuncNode ?? throw new Exception("函数根节点不可为null");
            ParseConfigFile(configName);
        }

        private void ParseConfigFile(string configName)
        {
            // 读取json文件
            string json = File.ReadAllText(configName);
            Dictionary<string, object> configRoot = GetDic(json);

            if (configRoot.ContainsKey("types"))
            {
                Dictionary<string, object> types = GetDic(configRoot["types"]);
                foreach(var kv in types)
                {
                    Types.Add(kv.Key, GetTypeItem(kv.Value));
                }
            }

            if (configRoot.ContainsKey("vars"))
            {
                Dictionary<string, object> vars = GetDic(configRoot["vars"]);
                foreach(var kv in vars)
                {
                    Vars.Add(kv.Key, GetVarItem(kv.Value));
                }
            }
        }

        private Dictionary<string, object> GetDic(object obj)
        {
            if(obj == null)
            {
                return null;
            }

            Dictionary<string, object> result = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(obj.ToString());

            if(result == null)
            {
                throw new Exception("对象不是字典类型");
            }
            else
            {
                return result;
            }
        }

        private VarItem GetVarItem(object obj)
        {
            var var = GetDic(obj);
            string type = null;
            if (var.ContainsKey("type"))
            {
                type = var["type"].ToString();
            }

            VarItem result = null;
            switch (type)
            {
                case "array":
                    {
                        TypeItem typeItem = null;
                        if (var.ContainsKey("customType"))
                        {
                            string typeItemName = var["customType"].ToString();
                            if (Types.ContainsKey(typeItemName))
                            {
                                typeItem = Types[typeItemName];
                            }
                        }
                        result = new ArrayVarItem(typeItem);
                        break;
                    }
                case "string":
                    {
                        var temp = new StringVarItem();

                        if (var.ContainsKey("maxLength"))
                        {
                            temp.MaxLength = int.Parse(var["maxLength"].ToString());
                        }

                        result = temp;
                        break;
                    }
                case "picture":
                    {
                        var temp = new PictureVarItem();

                        if (var.ContainsKey("width"))
                        {
                            temp.Width = double.Parse(var["width"].ToString());
                        }

                        if (var.ContainsKey("height"))
                        {
                            temp.Height = double.Parse(var["height"].ToString());
                        }

                        result = temp;
                        break;
                    }
                case "body":
                    {
                        var temp = new BodyVarItem();

                        if (var.ContainsKey("noFormat"))
                        {
                            temp.NoFormat = Boolean.Parse(var["noFormat"].ToString());
                        }

                        result = temp;
                        break;
                    }
                case "object":
                    {
                        TypeItem typeItem = null;
                        if (var.ContainsKey("customType"))
                        {
                            string typeItemName = var["customType"].ToString();
                            if (Types.ContainsKey(typeItemName))
                            {
                                typeItem = Types[typeItemName];
                            }
                        }
                        result = new ObjectVarItem(typeItem);
                        break;
                    }
            }

            if (var.ContainsKey("name"))
            {
                result.Name = var["name"].ToString();
            }

            if (var.ContainsKey("defaultValue"))
            {
                result.DefaultValueString = var["defaultValue"].ToString();
            }

            if (var.ContainsKey("computed"))
            {
                result.Computed = GetComputed(var["computed"].ToString(), _rootFuncNode);
            }

            return result;
        }

        private FuncNode GetComputed(string code, FuncNode rootFuncNode)
        {
            GlobalConfig globalConfig = new GlobalConfig
            {
                TemplateConfig = this,
                RootFuncNode = rootFuncNode
            };

            object formula = MarkerParser.ParseText(code, globalConfig, true);

            if(formula is FuncNode)
            {
                return formula as FuncNode;
            }
            else
            {
                throw new Exception(string.Format("表达式\"{0}\"必须是函数、变量或常量", code));
            }
        }

        private TypeItem GetTypeItem(object obj)
        {
            var type = GetDic(obj);
            TypeItem result = new TypeItem();

            if (type.ContainsKey("name"))
            {
                result.Name = type["name"].ToString();
            }

            if (type.ContainsKey("props"))
            {
                var props = GetDic(type["props"]);
                foreach(var kv in props)
                {
                    result.Props.Add(kv.Key, GetVarItem(kv.Value));
                }
            }

            return result;
        }

        public object FormatData(string key, object value)
        {
            return null;
        }

        public void SetDatas(Dictionary<string, object> datas)
        {
            Dictionary<string, DataItem> result = new Dictionary<string, DataItem>();

            // 先添加必须有的数据
            foreach(var kv in Vars)
            {
                if (datas.ContainsKey(kv.Key))
                {
                    object tempValue = null;
                    if(datas[kv.Key] is DataItem)
                    {
                        tempValue = (datas[kv.Key] as DataItem).GetFormatedDataValue();
                    }
                    else
                    {
                        tempValue = datas[kv.Key];
                    }

                    result[kv.Key] = new DataItem(tempValue, kv.Value);
                }
                else
                {
                    result[kv.Key] = new DataItem(null, kv.Value);
                }
            }

            // 再填补多余的数据
            foreach(var kv in datas)
            {
                if (!result.ContainsKey(kv.Key))
                {
                    if(datas[kv.Key] is DataItem)
                    {
                        result[kv.Key] = kv.Value as DataItem;
                    }
                    else
                    {
                        StringVarItem stringVarItem = new StringVarItem();
                        result[kv.Key] = new DataItem(datas[kv.Key], stringVarItem);
                    }
                }
            }

            // 将数据添加到rootFuncNode中
            foreach(var kv in result)
            {
                _rootFuncNode.SetData(kv.Key, kv.Value);
            }
        }

        public void SetDatas(Dictionary<string, DataItem> datas)
        {
            var temp = new Dictionary<string, object>();
            foreach(var kv in datas)
            {
                temp[kv.Key] = kv.Value;
            }

            SetDatas(temp);
        }
    }

    public class TypeItem: ItemBase
    {
        public Dictionary<string, VarItem> Props { get; set; }
        public VarItem DefaultConfig { get; set; }

        public TypeItem()
        {
            Props = new Dictionary<string, VarItem>();
            DefaultConfig = new ObjectVarItem(this);
        }
    }

    public class PictureVarItem: VarItem
    {
        public double Width { get; set; }
        public double Height { get; set; }

        public PictureVarItem()
            : base(UnitType.Picture)
        {

        }

        public override object FormatData(object data)
        {
            var pic = data as PictureUnit;
            if(Width > 0)
            {
                pic.Width = Width;
            }
            if (Height > 0)
            {
                pic.Height = Height;
            }
            return pic;
        }

        public override object MatchData(NodeBase nodeBase)
        {
            throw new NotImplementedException();
        }
    }

    public class StringVarItem : VarItem
    {
        public int MaxLength { get; set; }

        public StringVarItem()
            : base(UnitType.String)
        {

        }

        public override object FormatData(object data)
        {
            var str = data.ToString();
            if(str.Length > MaxLength && MaxLength > 0)
            {
                str = str.Substring(0, MaxLength);
            }
            return str;
        }

        public override object MatchData(NodeBase nodeBase)
        {
            throw new NotImplementedException();
        }
    }

    public class FullStringVarItem : VarItem
    {
        public int MaxLength { get; set; }

        public FullStringVarItem()
            : base(UnitType.FullString)
        {

        }

        public override object FormatData(object data)
        {
            FullString fullString = data as FullString;
            if(fullString == null)
            {
                fullString = new FullString();
                fullString.Items.Add(new TextRangeUnit { Text = data.ToString() });
            }
            return fullString.Items;
        }

        public override object MatchData(NodeBase nodeBase)
        {
            throw new NotImplementedException();
        }
    }

    public class BodyVarItem: VarItem
    {
        public bool NoFormat { get; set; }

        public BodyVarItem()
            : base(UnitType.Body)
        {

        }

        public override object FormatData(object data)
        {
            void RemoveSource(List<Unit> units)
            {
                foreach (var unit in units)
                {
                    unit.Source = null;
                    RemoveSource(unit.Children);

                }
            }

            if (NoFormat)
            {
                RemoveSource(data as List<Unit>);
            }

            return data;
        }

        public override object MatchData(NodeBase nodeBase)
        {
            throw new NotImplementedException();
        }
    }

    public class NumberVarItem : VarItem
    {
        public double Min { get; set; }
        public double Max { get; set; }

        public NumberVarItem()
            : base(UnitType.Number)
        {

        }

        public override object FormatData(object data)
        {
            if (data == null) return data;

            double num = (double)data;
            if(num > Max)
            {
                num = Max;
            }
            else if(num < Min)
            {
                num = Min;
            }
            return num;
        }

        public override object MatchData(NodeBase nodeBase)
        {
            object data = nodeBase.FuncNode.GetDataWithChildren(Name);
            return FormatDataOrDefault(data, nodeBase.FuncNode);
        }
    }

    public class ArrayVarItem : VarItem
    {
        public TypeItem CustomType { get; set; }

        public ArrayVarItem(TypeItem typeItem)
            : base(UnitType.Array)
        {
            CustomType = typeItem;
        }

        public override object FormatData(object data)
        {
            return data;
        }

        public override object MatchData(NodeBase nodeBase)
        {
            throw new NotImplementedException();
        }
    }

    public class ObjectVarItem : VarItem
    {
        public TypeItem CustomType { get; set; }

        public ObjectVarItem(TypeItem typeItem)
            : base(UnitType.Object)
        {
            CustomType = typeItem;
        }

        public override object FormatData(object data)
        {
            return data;
        }

        public override object MatchData(NodeBase nodeBase)
        {
            throw new NotImplementedException();
        }

        public override object DeserializeDefaultValue()
        {
            var result = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(DefaultValueString);
            return result;
        }
    }

    public abstract class VarItem: ItemBase
    {
        public FuncNode Computed { get; set; }

        public VarItem(UnitType unitType)
        {
            UnitType = unitType;
        }

        public override object GetDefaultValue(FuncNode rootFuncNode = null)
        {
            if(_defaultValue == null)
            {
                if(Computed != null)
                {
                    //if(rootFuncNode != null)
                    //{
                    //    Computed.Parent = rootFuncNode;
                    //}
                    _defaultValue = FormatData(Computed.Run(null, null));
                    //Computed.SetData(Name, new DataItem(_defaultValue, this));
                }
                else if (DefaultValueString != null)
                {
                    _defaultValue = DeserializeDefaultValue();
                }
            }
            return _defaultValue;
        }

        public abstract object FormatData(object data);

        public object FormatDataOrDefault(object data, FuncNode rootFuncNode = null)
        {
            object result = null;
            if(data == null)
            {
                result = GetDefaultValue(rootFuncNode);
            }
            else
            {
                result = FormatData(data);
            }
            return result;
        }

        public abstract object MatchData(NodeBase nodeBase);

        public object MatchDataOrDefault(NodeBase nodeBase, FuncNode rootFuncNode)
        {
            object result = MatchData(nodeBase);
            if (result == null)
            {
                result = GetDefaultValue(rootFuncNode);
            }
            return result;
        }
    }

    public class ItemBase
    {
        public string Name { get; set; }
        public string Alias { get; set; }
        public UnitType UnitType { get; protected set; }
        protected object _defaultValue = null;
        public string DefaultValueString { get; set; }
        public virtual object GetDefaultValue(FuncNode rootFuncNode = null)
        {
            if(_defaultValue == null && DefaultValueString != null)
            {
                _defaultValue = DeserializeDefaultValue();
            }
            return _defaultValue;
        }
        public void SetDefaultValue(object value)
        {
            _defaultValue = value;
        }
        public virtual object DeserializeDefaultValue()
        {
            return DefaultValueString;
        }
    }

    public class TemplateConfigOptions
    {
        public MarkerOption Marker { get; set; }

        public TemplateConfigOptions()
        {
            Marker = new MarkerOption();
        }
    }

    public class MarkerOption
    {
        public string Begin { get; set; }
        public string End { get; set; }
        public string Style { get; set; }
    }
}
